using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace PowerLife
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Color backColor = Color.Black;
        Song clip;
        bool songstart = false;

        //Textures (Probably add to a texture array or something when we have dozens of textures)
        Texture2D tLand;
        Texture2D tSky;
        Texture2D tWater;
        Texture2D tPlayer;

        //The main gameworld
        World gameWorld;
        //viewport for the gameWorld
        ViewPort mainViewPort;
        //world generator class (should this be a non-instantiated class?)
        WorldGen generator;

        //font object for text output
        SpriteFont font;

        //Represents the player
        Player player;

        // Keyboard states used to determine key presses
        KeyboardState currentKeyboardState;
        KeyboardState previousKeyboardState;

        // A movement speed for the player
        float playerMoveSpeed;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 480;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();


            Window.Title = "Power Life v0.000.02";

            //Initialize the player class
            player = new Player();

            // Set a constant player move speed
            playerMoveSpeed = .90f;

            base.Initialize();

            

            //create gameworld and viewport


            Vector2 startingViewPortLocation = new Vector2(500, 300);
            mainViewPort = new ViewPort(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, (int)startingViewPortLocation.X, (int)startingViewPortLocation.Y);


            int numChunksX = 100;
            int numChunksY = 100;
            int chunkSize = 100;
            int seed = 123456;

            generator = new WorldGen(numChunksX, numChunksY, chunkSize, 250, 500, 0.4f);
            generator.generate("theWorld", 12345);
            //generator.printGrid();
            generator = null;
            gameWorld = new World(numChunksX, numChunksY, chunkSize, seed, "theWorld", mainViewPort.xloc, mainViewPort.yloc);
            Vector2 playerPosition = new Vector2(mainViewPort.xloc + 20, mainViewPort.yloc + 20);
            player.Initialize(tPlayer, playerPosition);
            
            

        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            font = Content.Load<SpriteFont>("basicFont");
            tLand = Content.Load<Texture2D>("land2");
            tSky = Content.Load<Texture2D>("clouds");
            tWater = Content.Load<Texture2D>("water2");

            clip = Content.Load<Song>("clip");
            MediaPlayer.IsRepeating = true;

            // Load the player resources
            
            tPlayer = Content.Load<Texture2D>("player");
            

            
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            
            gameWorld.updateWorld(mainViewPort);

            

            if (!songstart)
            {
                MediaPlayer.Play(clip);
                songstart = true;
            }


            // Save the previous state of the keyboard so we can determinesingle key presses
            previousKeyboardState = currentKeyboardState;

            // Read the current state of the keyboard and store it
            currentKeyboardState = Keyboard.GetState();



            //Update the player
            UpdatePlayer(gameTime);

            //update camera based on keyboard input
            moveCamera(gameTime);


            base.Update(gameTime);
        }


        private void getMouseStatus(GameTime gameTime)
        {

            //MouseState mState = Mouse.GetState()
        }

        private void UpdatePlayer(GameTime gameTime)
        {
            // Use the Keyboard
            if (currentKeyboardState.IsKeyDown(Keys.A))
            {
                player.Position.X -= playerMoveSpeed;
            }
            if (currentKeyboardState.IsKeyDown(Keys.D))
            {
                player.Position.X += playerMoveSpeed;
            }
            if (currentKeyboardState.IsKeyDown(Keys.W))
            {
                player.Position.Y -= playerMoveSpeed;
            }
            if (currentKeyboardState.IsKeyDown(Keys.S))
            {
                player.Position.Y += playerMoveSpeed;
            }

            // Make sure that the player does not go out of bounds
            player.Position.X = MathHelper.Clamp(player.Position.X, 0, gameWorld.xChunks*gameWorld.ChunkSize);
            player.Position.Y = MathHelper.Clamp(player.Position.Y, 0, gameWorld.yChunks * gameWorld.ChunkSize);
        }

        /// <summary>
        /// moves camera based on camera input and world bounds.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        private void moveCamera(GameTime gameTime)
        {

            if (player.Active)
            {
                mainViewPort.xloc = player.Position.X;
                mainViewPort.yloc = player.Position.Y;
            }
            return;
            /*
            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right))
            {
                mainViewPort.acc = new Vector2(mainViewPort.acc.X + 3 * (float)gameTime.ElapsedGameTime.TotalSeconds, mainViewPort.acc.Y);
            }
            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left))
            {
                mainViewPort.acc = new Vector2(mainViewPort.acc.X - 3 * (float)gameTime.ElapsedGameTime.TotalSeconds, mainViewPort.acc.Y);
            }
            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up))
            {
                mainViewPort.acc = new Vector2( mainViewPort.acc.X, mainViewPort.acc.Y - 3 * (float)gameTime.ElapsedGameTime.TotalSeconds);
            }
            if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down))
            {
                mainViewPort.acc = new Vector2(mainViewPort.acc.X, mainViewPort.acc.Y + 3 * (float)gameTime.ElapsedGameTime.TotalSeconds);
            }
             */

            mainViewPort.acc = mainViewPort.acc / new Vector2(1.08f, 1.08f);

            mainViewPort.xloc += mainViewPort.acc.X;
            mainViewPort.yloc += mainViewPort.acc.Y;

            if (mainViewPort.xloc < 0)
            {
                mainViewPort.xloc = 0;
            }
            if (mainViewPort.yloc < 0)
            {
                mainViewPort.yloc = 0;
            }
            if (mainViewPort.yloc > gameWorld.yChunks*gameWorld.ChunkSize-32f)
            {
                mainViewPort.yloc = gameWorld.yChunks * gameWorld.ChunkSize - 32f;
            }
            if (mainViewPort.xloc > gameWorld.xChunks * gameWorld.ChunkSize-42.6666f)
            {
                mainViewPort.xloc = (gameWorld.xChunks * gameWorld.ChunkSize) - 42.6666f;
            }

        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            //Initializing values for loops
            int x = 0;
            int y = 0;
            int nodeSize = 15;

            //Begins new drawings
            spriteBatch.Begin();

            
            
            //Draw scrolling background.
            Vector2 skyloc = new Vector2((float)((0-mainViewPort.xloc) % 1000.0), 0);
            spriteBatch.Draw(tSky, skyloc, Color.White);


            //iterate through chunks
            for(int xchunk = 0; xchunk < gameWorld.chunkArray.GetLength(0); xchunk++)
            {


                for (int ychunk = 0; ychunk < gameWorld.chunkArray.GetLength(1); ychunk++ )
                {
                    Chunk currChunk = gameWorld.chunkArray[xchunk, ychunk];
                    if (!currChunk.loaded)
                    {
                        continue;
                    }
                    //check if current chunk needs to be rendered. If out of bounds, continue
                    if ((float)(currChunk.Xloc * nodeSize + currChunk.chunkSize * nodeSize) < mainViewPort.xloc * nodeSize)
                    {

                        continue;
                    }
                    //if beyond right bound
                    if ((float)(currChunk.Xloc * nodeSize) > (mainViewPort.xloc * nodeSize + mainViewPort.xsize))
                    {

                        continue;
                    }
                    //if beyond top bound
                    if ((float)(currChunk.Yloc * nodeSize + currChunk.chunkSize * nodeSize) < mainViewPort.yloc * nodeSize)
                    {

                        continue;
                    }
                    if ((float)(currChunk.Yloc * nodeSize) > (mainViewPort.yloc * nodeSize + mainViewPort.ysize))
                    {

                        continue;
                    }

                    //Attempt to render chunk. Currently just spriteBatch.Draws everything in chunk. Can likely be greatly optimized ( check nodes before drawing)
                    for (x = 0; x < gameWorld.ChunkSize; x++)
                    {
                        for (y = 0; y < gameWorld.ChunkSize; y++)
                        {
            
                            Vector2 corner = new Vector2();
                            corner.X = x * nodeSize + currChunk.Xloc * nodeSize - mainViewPort.xloc * nodeSize;
                            corner.Y = y * nodeSize + currChunk.Yloc * nodeSize - mainViewPort.yloc * nodeSize;

                            int nodeType = currChunk.GetNodeAtXY(x, y).GetNodeType();

                            if (nodeType == 1)
                            {
                                spriteBatch.Draw(tLand, corner, new Color(1.0f - mainViewPort.yloc/5000.0f,1.0f - mainViewPort.yloc/5000.0f,1.0f - mainViewPort.yloc/5000.0f));

                            }
                            else if (nodeType == 2)
                            {
                                spriteBatch.Draw(tWater, corner, new Color(.5f, .5f, .5f, 0.5f));

                            }
                            else if (nodeType == 0)
                            {
                                //Renders sky tiles. Comment out to use scrolling background.
                                //spriteBatch.Draw(tSky, corner, Color.White);
                            }
                            else
                            {
                                System.Diagnostics.Debug.WriteLine("Error: Invalid nodeType");
                            }
                        }
                    }
                }
  

            }

            //draw debug text
            spriteBatch.DrawString(font, mainViewPort.xloc.ToString(), new Vector2(0, 0), Color.White);
            spriteBatch.DrawString(font, mainViewPort.yloc.ToString(), new Vector2(100, 0), Color.White);
            spriteBatch.DrawString(font, gameTime.ElapsedGameTime.TotalMilliseconds.ToString(), new Vector2(200, 0), Color.White);

            player.Draw(spriteBatch, mainViewPort);

            //Needs to end otherwise constant state of drawing, aka crash
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}
